/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.skatiger.jrove.impl.jaxb;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.skatiger.jrove.ResourceResolver;
import org.skatiger.jrove.TagLibrary;
import org.skatiger.jrove.impl.DefaultConfigurator;
import org.skatiger.jrove.util.ClassUtil;
import org.skatiger.jrove.util.Classpath;

public class JaxbConfigReader {
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory
            .getLogger(JaxbConfigReader.class);
    public final static String TAGLIB_SUFFIX = ".taglib.xml";
    public final static String TAGLIB_CONTEXT_PATH = "org.skatiger.jrove.impl.jaxb.taglib";
    private final JaxbComponentProvider componentProvider;
    private final JaxbRendererProvider rendererProvider;
    private final List<TagLibrary> tagLibraries;

    public JaxbConfigReader() {
        super();
        tagLibraries = new ArrayList<TagLibrary>();
        componentProvider = new JaxbComponentProvider();
        rendererProvider = new JaxbRendererProvider();
    }

    public List<TagLibrary> getTagLibraries() {
        return tagLibraries;
    }

    public void init(DefaultConfigurator configurator) throws Exception {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        JaxbTaglibConfig taglibConfig = new JaxbTaglibConfig(configurator);
        URL[] taglibs = Classpath.search(cl, "META-INF/jrove/", TAGLIB_SUFFIX);
        for (URL url : taglibs) {
            processTaglib(taglibConfig, url);
        }
        String[] libs = configurator.getLibraries();
        for (int i = 0; i < libs.length; i++) {
            try {
                ResourceResolver resolver = configurator.getResourceResolver();
                URL src = resolver.resolveUrl(libs[i].trim());
                if (src == null) {
                    throw new FileNotFoundException(libs[i]);
                }
                processTaglib(taglibConfig, src);
            } catch (IOException e) {
                logger.warn("Error Loading Library: " + libs[i], e);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> Class<T> createClass(Class<T> type, String name)
            throws ClassNotFoundException {
        Class<?> factory = null;
        factory = ClassUtil.forName(name.trim());
        if (!type.isAssignableFrom(factory)) {
            throw new ClassCastException(name + " must be an instance of "
                    + type.getName());
        }
        return (Class<T>) factory;
    }

    static Method createMethod(Class<?> type, String s) throws Exception {
        int pos = s.indexOf(' ');
        if (pos == -1) {
            throw new Exception("Must Provide Return Type: " + s);
        } else {
            int pos2 = s.indexOf('(', pos + 1);
            if (pos2 == -1) {
                throw new Exception(
                        "Must provide a method name, followed by '(': " + s);
            } else {
                String mn = s.substring(pos + 1, pos2).trim();
                pos = s.indexOf(')', pos2 + 1);
                if (pos == -1) {
                    throw new Exception("Must close parentheses, ')' missing: "
                            + s);
                } else {
                    String[] ps = s.substring(pos2 + 1, pos).trim().split(",");
                    Class<?>[] pc;
                    if (ps.length == 1 && "".equals(ps[0])) {
                        pc = new Class[0];
                    } else {
                        pc = new Class[ps.length];
                        for (int i = 0; i < pc.length; i++) {
                            pc[i] = ClassUtil.forName(ps[i].trim());
                        }
                    }
                    try {
                        return type.getMethod(mn, pc);
                    } catch (NoSuchMethodException e) {
                        throw new Exception("No Function Found on type: "
                                + type.getName() + " with signature: " + s);
                    }

                }

            }
        }
    }

    void processTaglib(JaxbTaglibConfig taglibConfig, URL url) throws Exception {
        TagLibrary library = taglibConfig.create(url);
        tagLibraries.add(library);
        if (logger.isInfoEnabled()) {
            logger.info("Successfully Loaded Library: " + url);
        }
    }

    public JaxbComponentProvider getComponentProvider() {
        return componentProvider;
    }

    public JaxbRendererProvider getRendererProvider() {
        return rendererProvider;
    }

}
